package com.bpmn.flow.cmd;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.bpmn.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.HistoricActivityInstanceEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.history.HistoricTaskInstance;

import java.util.ArrayList;
import java.util.List;

/**
 * @author rain
 * @description 任务撤回Command类
 * @date 2025年02月17日 15:36
 */
@AllArgsConstructor
public class TaskRecallCmd implements Command<Void> {

    /**
     * 任务编号
     */
    protected final String taskId;

    /***
     * 办理人
     */
    protected final String userId;

    @Override
    public Void execute(CommandContext commandContext) {
        if (taskId == null) {
            throw new FlowableIllegalArgumentException("taskId不能为空!");
        }
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        //获取历史服务
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        //根据任务id查询历史任务
        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        //进行一系列任务和流程校验
        basicCheck(runtimeService, task);
        //获取部署模型
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(task.getProcessDefinitionId());
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        List<String> nextElementIdList = new ArrayList<>();
        List<UserTask> nextUserTaskList = new ArrayList<>();
        //先获取后续节点信息
        getNextElementInfo(bpmnModel, flowElement, nextElementIdList, nextUserTaskList);
        //再校验后续节点任务是否已经办理完成
        existNextFinishedTaskCheck(historyService, task, nextUserTaskList);
        //清理节点历史
        deleteHistoricActivityInstance(processEngineConfiguration, historyService, task);
        //执行跳转
        List<String> recallTaskList = getRecallTaskList(runtimeService, task, nextElementIdList);
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(recallTaskList, task.getTaskDefinitionKey())
                .changeState();
        return null;
    }

    /***
     * 任务校验
     * @param runtimeService 运行时服务
     * @param task 当前任务
     * @author rain
     */
    private void basicCheck(RuntimeService runtimeService, HistoricTaskInstance task) {
        if (task == null) {
            throw new FlowableObjectNotFoundException("任务不存在!");
        }
        if (ObjectUtil.notEqual(userId, task.getAssignee())) {
            throw new ServiceException("您不是该任务的办理人，无法撤销");
        }
        if (task.getEndTime() == null) {
            throw new FlowableException("任务正在执行，不需要回退!");
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).singleResult();
        if (processInstance == null) {
            throw new FlowableException("该流程已经结束，无法进行任务回退!");
        }
    }

    /***
     *  获取后续节点信息
     * @param bpmnModel 流程模型
     * @param currentNode 当前节点
     * @param nextNodeIdList 后续节点id集合
     * @param nextUserTaskList 后续用户任务节点集合
     * @author rain
     */
    private void getNextElementInfo(BpmnModel bpmnModel, FlowElement currentNode, List<String> nextNodeIdList, List<UserTask> nextUserTaskList) {
        List<SequenceFlow> outgoingFlows = ((FlowNode) currentNode).getOutgoingFlows();
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            //后续节点
            FlowElement targetFlowElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());
            nextNodeIdList.add(targetFlowElement.getId());
            if (targetFlowElement instanceof UserTask) {
                nextUserTaskList.add((UserTask) targetFlowElement);
            } else if (targetFlowElement instanceof Gateway gateway) {
                //网关节点执行递归操作
                getNextElementInfo(bpmnModel, gateway, nextNodeIdList, nextUserTaskList);
            } else if (targetFlowElement instanceof ServiceTask serviceTask) {
                // 服务任务执行递归操作
                getNextElementInfo(bpmnModel, serviceTask, nextNodeIdList, nextUserTaskList);
            }
        }
    }

    /***
     * 校验后续节点任务是否已经办理完成
     * @param historyService 历史服务
     * @param currentTaskInstance  当前任务实例
     * @param nextUserTaskList 后续用户任务节点集合
     * @author rain
     */
    private void existNextFinishedTaskCheck(HistoryService historyService, HistoricTaskInstance currentTaskInstance, List<UserTask> nextUserTaskList) {
        List<HistoricTaskInstance> historyTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(currentTaskInstance.getProcessInstanceId())
                .taskCompletedAfter(currentTaskInstance.getEndTime())
                .list();
        List<String> nextUserTaskIdList = CollStreamUtil.toList(nextUserTaskList, UserTask::getId);
        if (!historyTaskInstanceList.isEmpty()) {
            for (HistoricTaskInstance historicTaskInstance : historyTaskInstanceList) {
                if (nextUserTaskIdList.contains(historicTaskInstance.getTaskDefinitionKey())) {
                    throw new FlowableException("后续节点任务已经办理完成，无法进行回退!");
                }
            }
        }
    }

    /***
     * 清理节点历史
     * @param processEngineConfiguration 引擎配置
     * @param task 当前任务
     * @param historyService 历史服务
     * @author rain
     */
    private void deleteHistoricActivityInstance(ProcessEngineConfigurationImpl processEngineConfiguration, HistoryService historyService, HistoricTaskInstance task) {
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .activityId(task.getTaskDefinitionKey()).list();
        HistoricActivityInstance historicActivity = CollUtil.findOne(activityInstanceList, instance -> task.getId().equals(instance.getTaskId()));
        HistoricActivityInstanceEntityManager hisInstanceEntityManager = processEngineConfiguration.getHistoricActivityInstanceEntityManager();
        hisInstanceEntityManager.delete(historicActivity.getActivityId());
        //删除被撤回的节点的历史
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .startedAfter(task.getEndTime())
                .orderByHistoricActivityInstanceStartTime()
                .asc().list();
        List<String> deleteHisActivityIdList = new ArrayList<>();
        if (CollUtil.isNotEmpty(historicActivityInstanceList)) {
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
                if (!deleteHisActivityIdList.contains(historicActivityInstance.getActivityId())) {
                    deleteHisActivityIdList.add(historicActivityInstance.getActivityId());
                    hisInstanceEntityManager.delete(historicActivityInstance.getActivityId());
                }
            }
        }
    }

    /***
     * 获取可撤回的节点列表
     * @param runtimeService 运行服务
     * @param task 当前任务
     * @param nextElementIdList 后续节点id集合
     * @return {@link List} {@link String}
     * @author rain
     */
    private List<String> getRecallTaskList(RuntimeService runtimeService, HistoricTaskInstance task, List<String> nextElementIdList) {
        List<String> recallTaskList = new ArrayList<>();
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processDefinitionId(task.getProcessDefinitionId())
                .onlyChildExecutions().list();
        if (CollUtil.isNotEmpty(executions)) {
            for (Execution execution : executions) {
                if (CollUtil.contains(nextElementIdList, execution.getActivityId())) {
                    recallTaskList.add(execution.getActivityId());
                }
            }
        }
        return recallTaskList;
    }
}
